using System;
using System.Collections.Specialized;
using System.Data;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
using System.Xml;
using Microsoft.ApplicationBlocks.ExceptionManagement;
using Dimok;

namespace Dimok.Exceptions{

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Exception for signalling client errors. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	[Serializable]
	public class ClientException : ASPException{

		#region Member Variables
		private string message;
		public string messageOriginal;
		NameValueCollection additionalInfo;
		DataSet ReturnDataSet;
		#endregion

		#region Constructors

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="inner">	The inner. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(Exception inner):this(inner,(NameValueCollection)null){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="info">	The information. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(NameValueCollection info):this((Exception)null,info){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">				The exc. </param>
		/// <param name="namedVaues">	A variable-length parameters list containing named vaues. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(Exception exc, params string[] namedVaues) : this(exc, new NameValueCollectionEx(namedVaues) as NameValueCollection) { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="inner">	The inner. </param>
		/// <param name="info">		The information. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(Exception inner,NameValueCollection info):this("",null as DataSet,inner,info){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(string message):this(message,(NameValueCollection)null){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		/// <param name="info">			The information. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(string message,NameValueCollection info):this(message,null,info){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		/// <param name="inner">		The inner. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(string message, Exception inner) : this(message, inner, new NameValueCollection()) { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataView">	The data view. </param>
		/// <param name="inner">		The inner. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(DataView dataView, Exception inner) : this(dataView.Table, inner) { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataTable">	The data table. </param>
		/// <param name="inner">			The inner. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(DataTable dataTable, Exception inner) : this(dataTable.DataSet, inner) { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataSet">	Set the data belongs to. </param>
		/// <param name="inner">		The inner. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(DataSet dataSet, Exception inner) : this("",dataSet,inner,null as NameValueCollection) {}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		/// <param name="inner">		The inner. </param>
		/// <param name="info">			The information. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(string message, Exception inner, NameValueCollection info): this(message,null, inner, info) {}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		/// <param name="dataSet">	Set the data belongs to. </param>
		/// <param name="inner">		The inner. </param>
		/// <param name="info">			The information. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(string message,DataSet dataSet, Exception inner, NameValueCollection info)	: base(message, inner, info) {
			this.message = messageOriginal = message;
			ReturnDataSet = dataSet;
			SetMessage(inner);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="info">			The information. </param>
		/// <param name="context">	The context. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ClientException(SerializationInfo info, StreamingContext context) : base (info, context) {  
			message = info.GetString("message");
			messageOriginal = info.GetString("messageOriginal");
		}
	#endregion

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Override the GetObjectData method to serialize custom values. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="info">			Represents the SerializationInfo of the exception. </param>
		/// <param name="context">	Represents the context information of the exception. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			// Serialize this class' state and then call the base class GetObjectData
			info.AddValue("message", message, typeof(string));
			info.AddValue("messageOriginal", messageOriginal, typeof(string));
			base.GetObjectData(info, context);
		}

		#region Get/Set Message(s)/Exceptions

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a message that describes the current exception. </summary>
		///
		/// <value>	The message. </value>
		///
		/// ### <returns>	
		/// The error message that explains the reason for the exception, or an empty string(""). 
		/// </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public override string Message {get {return message;}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the message raw. </summary>
		///
		/// <value>	The message raw. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string MessageRaw {get {return messageOriginal;}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Sets a message. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exception">	The exception. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void SetMessage(Exception exception) { SetMessage(GetMessages(exception)); }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Sets a message. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void SetMessage(string message){
			Regex re = new Regex(@"\s+The transaction ended in the trigger. The batch has been aborted.",RegexOptions.Compiled);
			message = re.Replace(message, "");
			messageOriginal = this.Message + Environment.NewLine + message;
			this.message = GetXMLMessage(messageOriginal);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the messages. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">	The exc. </param>
		///
		/// <returns>	The messages. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public new static string GetMessages(Exception exc){
			if( exc == null )return "";
			ArrayList alMsg = new ArrayList();
			do{
				if( exc.GetType() != typeof(HiddenException) )
					alMsg.Add(typeof(ClientException).IsInstanceOfType(exc) ? ((ClientException)exc).MessageRaw : exc.Message);
			}while( (exc = exc.InnerException) != null );
			return CleanMessage( string.Join(Environment.NewLine,(string[])alMsg.ToArray(typeof(string))));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the properties. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exception">	The exception. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void GetProperties(Exception exception) {
			if (additionalInfo == null) additionalInfo = new NameValueCollection();
			Exception currentException = exception;	// Temp variable to hold InnerException object during the loop.
			while(currentException != null) {
				#region Loop through the public properties of the exception object and record their value
				// Loop through the public properties of the exception object and record their value.
				PropertyInfo[] aryPublicProperties = currentException.GetType().GetProperties();
				foreach (PropertyInfo p in aryPublicProperties) {
					// Do not log information for the InnerException or StackTrace. This information is 
					// captured later in the process.
					if( p.Name.StartsWith("message") )continue;
					if (p.Name != "InnerException" && p.Name != "StackTrace") {
						if (p.GetValue(currentException, null) == null) continue;
						else {
							// Loop through the collection of AdditionalInformation if the exception type is a BaseApplicationException.
							if (p.Name == "additionalInfo" && currentException is ClientException) {
								if (p.GetValue(currentException, null) != null)
									// Cast the collection into a local variable.
									additionalInfo.Add((NameValueCollection)p.GetValue(currentException, null));
							}
								// Otherwise just write the ToString() value of the property.
							else {
								additionalInfo.Add( p.Name, p.GetValue(currentException, null)+""   );
							}
						}
					}
				}
				#endregion
				if( currentException is ClientException )return;
				currentException = currentException.InnerException;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the exceptions. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exception">	The exception. </param>
		///
		/// <returns>	The exceptions. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string GetExceptions(Exception exception){
			const string TEXT_SEPARATOR = "*********************************************";
			#region Loop through each exception class in the chain of exception objects
			// Loop through each exception class in the chain of exception objects.
			Exception currentException = exception;	// Temp variable to hold InnerException object during the loop.
			int intExceptionCount = 1;				// Count variable to track the number of exceptions in the chain.
			StringBuilder strInfo = new StringBuilder();
			do {
				// Write title information for the exception object.
				strInfo.AppendFormat("{0}{0}{1}) Exception Information{0}{2}", Environment.NewLine, intExceptionCount.ToString(), TEXT_SEPARATOR);
				strInfo.AppendFormat("{0}Exception Type: {1}", Environment.NewLine, currentException.GetType().FullName);
				
				#region Loop through the public properties of the exception object and record their value
				// Loop through the public properties of the exception object and record their value.
				PropertyInfo[] aryPublicProperties = currentException.GetType().GetProperties();
				NameValueCollection currentAdditionalInfo;
				foreach (PropertyInfo p in aryPublicProperties) {
					// Do not log information for the InnerException or StackTrace. This information is 
					// captured later in the process.
					if (p.Name != "InnerException" && p.Name != "StackTrace"){
						if (p.GetValue(currentException,null) == null) {
							continue;
							strInfo.AppendFormat("{0}{1}: NULL", Environment.NewLine, p.Name);
						}
						else {
							// Loop through the collection of AdditionalInformation if the exception type is a BaseApplicationException.
							if (p.Name == "AdditionalInformation" && currentException is BaseApplicationException) {
								// Verify the collection is not null.
								if (p.GetValue(currentException,null) != null) {
									// Cast the collection into a local variable.
									currentAdditionalInfo = (NameValueCollection)p.GetValue(currentException,null);

									// Check if the collection contains values.
									if (currentAdditionalInfo.Count > 0) {
										strInfo.AppendFormat("{0}AdditionalInformation:", Environment.NewLine);

										// Loop through the collection adding the information to the string builder.
										for (int i = 0; i < currentAdditionalInfo.Count; i++) {
											strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, currentAdditionalInfo.GetKey(i), currentAdditionalInfo[i]);
										}
									}
								}
							}
								// Otherwise just write the ToString() value of the property.
							else {
								strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, p.Name, p.GetValue(currentException,null));
							}
						}
					}
				}
				#endregion
				#region Record the Exception StackTrace
				// Record the StackTrace with separate label.
				if (currentException.StackTrace != null) {
					strInfo.AppendFormat("{0}{0}StackTrace Information{0}{1}", Environment.NewLine, TEXT_SEPARATOR);
					strInfo.AppendFormat("{0}{1}", Environment.NewLine, GetStackTrace(currentException));
				}
				#endregion

				// Reset the temp exception object and iterate the counter.
				currentException = currentException.InnerException;
				intExceptionCount++;
			} while (currentException != null);
			return strInfo.ToString();
			#endregion
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a stack trace. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">	The exc. </param>
		///
		/// <returns>	The stack trace. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string GetStackTrace(Exception exc){
			if( exc.StackTrace.Length > 0 )return exc.StackTrace;
			if( exc.InnerException == null )return "";
			return GetStackTrace(exc.InnerException);
		}
		#endregion

		#region Publish

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Publishes this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">	The exc. </param>
		/// <param name="nvc">	Information describing the additional. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void Publish(Exception exc,NameValueCollection nvc){ASPException.Publish(exc,nvc);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Publishes this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">	The exc. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void Publish(Exception exc){ASPException.Publish(exc);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Publishes this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="exc">			The exc. </param>
		/// <param name="call911">	true to call 911. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void Publish(Exception exc,bool call911){ASPException.Publish(exc,call911);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Publishes this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static void Publish(string message){ASPException.Publish(new Exception(message));}
		#endregion

		#region Privates ===========

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets an xml message. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="message">	The message. </param>
		///
		/// <returns>	The xml message. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private string GetXMLMessage(string message){
			ClientExceptionDS cds = new ClientExceptionDS();
			ClientExceptionDS.ClientExceptionDataSetRow cdsRow = cds.ClientExceptionDataSet.NewClientExceptionDataSetRow();
			//cdsRow.Message = System.Web.HttpUtility.UrlEncode(message).Replace("+"," ");

			cdsRow.Message = System.Web.HttpUtility.HtmlEncode(message.Replace("\nThe transaction ended in the trigger. The batch has been aborted.",""));
			cdsRow.Message = System.Web.HttpUtility.HtmlEncode(cdsRow.Message.Replace("\nThe ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.", ""));
			cdsRow.Message = System.Web.HttpUtility.HtmlEncode(cdsRow.Message.Replace("\nThe statement has been terminated.", ""));
			
			//return cdsRow.Message;
			cds.ClientExceptionDataSet.AddClientExceptionDataSetRow(cdsRow);
			cdsRow.AcceptChanges();
			if (ReturnDataSet != null) cds.Merge(ReturnDataSet);
			return cds.GetXml().Replace(" xml:space=\"preserve\"", "");
		}
		#endregion Privates ===========
	}
}